package com.shzq.service.impl;

import com.alibaba.fastjson.JSON;
import com.shzq.pojo.Node;
import com.shzq.service.ArithmeticService;
import com.shzq.service.RedisService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.geo.Circle;
import org.springframework.data.geo.GeoResults;
import org.springframework.data.geo.Point;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.BoundZSetOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j(topic = "ArithmeticServiceImpl")
public class ArithmeticServiceImpl implements ArithmeticService {

    public static final String DB_SCAN_DATA = "DBSCAN_data";

    @Value("${radius}")
    private int radius;

    @Value("${count}")
    private int count;
    @Autowired
    private RedisService redisService;

    @Autowired
    private StringRedisTemplate redisTemplate;
    @Override
    public List<List<double[]>> doDbScan() {
        String data = redisTemplate.boundValueOps(DB_SCAN_DATA).get();
        if(data==null){
            List<List<Node>> freshData = createData();
            return freshData.stream().map(list ->
                    list.stream().map(node ->{
                        Point point = node.getPoint();
                        return new double[]{point.getX(),point.getY()};
                    }).collect(Collectors.toList())
            ).collect(Collectors.toList());
        }else {
            List list = JSON.parseObject(data, List.class);
            List<List<double[]>> listList = new ArrayList<>();
            for (Object objList : list) {
                List<Node> nodes = JSON.parseArray(objList.toString(), Node.class);
                List<double[]> ats = nodes.stream().map(node -> {
                    Point point = node.getPoint();
                    return new double[]{point.getX(), point.getY()};
                }).collect(Collectors.toList());
                listList.add(ats);
            }
            return listList;
        }
    }

    @Override
    public List<List<Node>> createData(){
        long beginCreateData = System.currentTimeMillis();
        System.out.println("开始准备需要的数据");
        BoundZSetOperations<String, String> latitude = redisTemplate.boundZSetOps("latitude");
        Set<String> range = latitude.range(0, 1);
        if(range==null||range.size()==0){
            redisService.importData();
        }
        Set<Node> points = redisService.getAllPointMap();
        Map<String, Node> pointMap = redisService.getPointMap();
        Map <String, List<Node>> result = new HashMap<>();
        //遍历点集  在圆里面的点，如果个数超过count，则这些点都满足条件，我们需要对这些外围的点进行延伸
        Set<Node> nowNode = points;
        /*
        结果集要满足分类，在同一个圈子里的点作为一类
         */
        long begin = System.currentTimeMillis();
        System.out.println("准备数据耗时：" + ((begin - beginCreateData)/1000) + "s");
        System.out.println("算法开始时间为:" + begin);
        while(true){
            Set<Node> nextNode = new HashSet<>();
            if(nowNode !=null && nowNode.size() != 0){
                for (Node node : points) {
                    if(node.isVisited()){
                        continue;
                    }
                    node.setVisited(true);
                    String currentKey = node.getKey();
                    Point currentPoint = node.getPoint();
                    if(currentPoint == null) {
                        continue;
                    }
                    Circle circle = new Circle(currentPoint,radius);
                    //获得这个点为圆心的范围里面的所有点的key的集合
                    GeoResults<RedisGeoCommands.GeoLocation<String>> results = redisTemplate.boundGeoOps("latitude").radius(circle);
                    if(results == null) continue;
                    List<String> keys = results.getContent().stream().map(geo->geo.getContent().getName()).collect(Collectors.toList());
                    if(keys.size() > count) {
                        List<Node> nodes = result.get(currentKey);
                        if(nodes == null){
                            List<Node> child = new LinkedList<>();
                            child.add(node);
                            result.put(currentKey,child);
                        }
                        keys.forEach(key ->{
                            Node n = pointMap.get(key);
                            if(n.isVisited()) {
                                nextNode.add(n);
                            }
                        });
                    }
                }
                nowNode = nextNode;
            }else {
                break;
            }
        }
        long end = System.currentTimeMillis();
        System.out.println("算法结束时间为:" + end);
        System.out.println("算法用了：" + ((end - begin)/1000L) + "s");
        Collection<List<Node>> values = result.values();
        redisTemplate.boundValueOps(DB_SCAN_DATA).set(JSON.toJSONString(result));
        return new ArrayList<>(values);
    }
}